/**
 * Project: saasoa Copyright 2013 Top Digital Technology Co. Ltd. All right reserved.
 */
package com.topdt.jbpm.web.servlet.action;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.thx.common.util.AppContext;
import com.topdt.jbpm.dto.Coord;
import com.topdt.jbpm.dto.Path;
import com.topdt.jbpm.dto.Props;
import com.topdt.jbpm.dto.Rect;
import com.topdt.jbpm.model.FlowDetail;
import com.topdt.jbpm.web.servlet.JbpmServlet;
import com.topdt.jbpm.web.servlet.view.AssignmentHandler;
import com.topdt.jbpm.web.servlet.view.EventListener;
import com.topdt.jbpm.web.servlet.view.Handler;
import com.topdt.jbpm.web.servlet.view.Label;
import com.topdt.jbpm.web.servlet.view.On;
import com.topdt.jbpm.web.servlet.view.Process;
import com.topdt.jbpm.web.servlet.view.Transition;

/**
 * 建立日期 : 2013-7-17 上午11:06:36<br>
 * 作者 : balance<br>
 * 模块 : <br>
 * 描述 : <br>
 * 修改历史: 序号 日期 修改人 修改原因 <br>
 * 1 <br>
 * 2 <br>
 */
public class DefineFlowAction extends JbpmBaseAction {

  public DefineFlowAction(JbpmServlet servlet) {
    super(servlet);
  }

  List<Rect> rectList = new ArrayList<Rect>();
  List<Path> pathList = new ArrayList<Path>();
  List<Props> propsList = new ArrayList<Props>();
  Map<String, Rect> rectMap = new HashMap<String, Rect>();
  Map<String, String> pathMap = new IdentityHashMap<String, String>();

  /**
   * 保存
   *
   * @param request
   * @param response
   * @throws UnsupportedEncodingException
   */
  public void deploy(HttpServletRequest request, HttpServletResponse response)
      throws UnsupportedEncodingException {
    logger.debug("<进入>:deploy方法");
    String flowJson = request.getParameter("flow");
    flowJson = flowJson.replaceAll("~", "%");
    flowJson = URLDecoder.decode(flowJson, "UTF-8");
    flowJson = flowJson.replaceAll("\\^", "\'");
    String detailId = request.getParameter("detailId");
    String flowName = request.getParameter("flowName");
    String res = jbpmService.checkFlowName(detailId, flowName);
    FlowDetail detail = jbpmService.findFlowDetailByDetailId(detailId);
    String json = "";
    if (res.equals("success")) {
      // 1、json 转 xml 并保存为文件,json 和xml分别保存，为两个文件
      try {
        jsonToXml(flowJson, detailId);
        // 给rectList重新排序
        getSort();
        // 修改path实体里的to属性
        modifyPathTo();
        // 得到xml
        byte[] xml = getXml();
        // 保存数据
        String flowDetail = saveData(flowJson, detailId, xml);
        json = flowDetail + ";保存成功!";

        // TODO 此处记录日志

      } catch (Exception e) {
        json = ";保存失败!";
        logger.error("<错误>:保存失败", e);
      }
    } else {
      json = ";该流程名称已存在!";
    }
    this.responseJson(response, json);
  }

  /**
   * 显示xml
   *
   * @param request
   * @param response
   */
  public void showXml(HttpServletRequest request, HttpServletResponse response) {
    logger.debug("<进入>:showXml方法");
    String flowJson = request.getParameter("flow");
    flowJson = flowJson.replaceAll("~", "%");
    try {
      flowJson = URLDecoder.decode(flowJson, "UTF-8");
    } catch (UnsupportedEncodingException e1) {
      // TODO Auto-generated catch block
      logger.error("<错误>:showXml失败", e1);
    }
    String detailId = request.getParameter("detailId");
    String json = "";
    try {
      jsonToXml(flowJson, detailId);
      // 修改path实体里的to属性
      modifyPathTo();
      // 得到xml
      byte[] xml = getXml();
      json = new String(xml, "utf-8");
    } catch (Exception e) {
      logger.error("<错误>:showXml失败", e);
    }
    this.responseJson(response, json);
  }


  /**
   * @Description : 新建/修改流程
   * @Method_Name : getFlow
   * @param request
   * @param response
   * @return : void
   * @Creation Date : 2015年3月25日 上午10:43:05
   * @version : v1.00
   * @Author :
   *
   * @Update Date : 2015年3月25日 上午10:43:05
   * @Update Author : calonlan
   */
  public void getFlow(HttpServletRequest request, HttpServletResponse response) {
    logger.debug("<进入>:getFlow方法");
    String detailId = request.getParameter("detailId");
    FlowDetail flowDetail = this.jbpmService.findFlowDetailByDetailId(detailId);
    request.setAttribute("flowDetail", flowDetail);
    responseJsp(request, response, "/workflow/flowDefin/editor.jsp");
  }

  private void jsonToXml(String json, String detailId) throws JAXBException, IOException {
    logger.debug("<进入>:jsonToXml方法");
    JSONObject jsonObject = JSON.parseObject(json);
    Iterator<String> iterator = jsonObject.keySet().iterator();
    for (; iterator.hasNext();) {
      String param = iterator.next();
      if (param.equals("states")) {
        JSONObject jsonObject1 = (JSONObject) jsonObject.get(param);
        Iterator<String> iterator1 = jsonObject1.keySet().iterator();
        for (; iterator1.hasNext();) {
          String param1 = iterator1.next();
          Rect rect = JSONObject.parseObject(jsonObject1.getString(param1), Rect.class);
          rectList.add(rect);
          rectMap.put(param1, rect);
        }
      }
      if (param.equals("paths")) {
        JSONObject jsonObject2 = (JSONObject) jsonObject.get(param);
        Iterator<String> iterator2 = jsonObject2.keySet().iterator();
        for (; iterator2.hasNext();) {
          String param2 = iterator2.next();
          Path path = JSONObject.parseObject(jsonObject2.getString(param2), Path.class);
          pathList.add(path);
        }
      }
      if (param.equals("props")) {
        JSONObject jsonObject3 = (JSONObject) jsonObject.get(param);
        Iterator<String> iterator3 = jsonObject3.keySet().iterator();
        for (; iterator3.hasNext();) {
          String param3 = iterator3.next();
          Props props = JSONObject.parseObject(jsonObject3.getString(param3), Props.class);
          propsList.add(props);
        }
      }

    }
  }

  /**
   * 修改path实体里的to属性
   */
  private void modifyPathTo() {
    logger.debug("<进入>:modifyPathTo方法");
    Iterator<String> iterator2_1 = rectMap.keySet().iterator();
    for (; iterator2_1.hasNext();) {
      String s = iterator2_1.next();
      for (int i = 0; i < pathList.size(); i++) {
        if (pathList.get(i).getTo().equals(s)) {
          pathMap.put(pathList.get(i).getFrom(), pathList.get(i).getTo());
          pathList.get(i).setTo(rectMap.get(s).getProps().getText().getValue());
        }
      }
    }
  }

  /**
   * 给List<Rect> rectList排序
   */
  private void getSort() {
    logger.debug("<进入>:getSort方法");
    List<String> fromList = new ArrayList<String>();
    List<String> toList = new ArrayList<String>();
    List<String> allList = new ArrayList<String>();
    String first = "";
    for (Path path : pathList) {
      fromList.add(path.getFrom());
      toList.add(path.getTo());
      if (!allList.contains(path.getFrom())) {
        allList.add(path.getFrom());
      }
      if (!allList.contains(path.getTo())) {
        allList.add(path.getTo());
      }
    }
    for (int i = 0; i < fromList.size(); i++) {
      for (int j = 0; j < toList.size(); j++) {
        if (!toList.contains(fromList.get(i))) {
          first = fromList.get(i);
        }
      }
    }
    Path firstPath = findPathByFirst(first);
    List<String> listString = findNext(firstPath, allList);
    // 通过排序，对rectList进行重新赋值
    rectList.clear();
    for (String key : listString) {
      rectList.add(rectMap.get(key));
    }
  }

  /**
   * 找到第一个Path
   *
   * @param first
   * @return
   */
  private Path findPathByFirst(String first) {
    logger.debug("<进入>:findPathByFirst方法");
    Path firstPath = new Path();
    for (Path path : pathList) {
      if (path.getFrom().equals(first)) {
        firstPath = path;
      }
    }
    return firstPath;
  }

  /**
   * 找到后面的path顺序，并存放到listString中
   *
   * @param firstPath
   * @param allList
   */
  private List<String> findNext(Path firstPath, List<String> allList) {
    logger.debug("<进入>:findNext方法");
    List<String> listString = new ArrayList<String>();
    List<String> newPathList = new ArrayList<String>();
    List<String> oldPathList = new ArrayList<String>();
    String to = firstPath.getTo();
    listString.add(firstPath.getFrom());
    listString.add(to);
    for (int j = 0; j <= allList.size(); j++) {
      newPathList.clear();
      for (String s : oldPathList) {
        newPathList.add(s);
      }
      oldPathList.clear();
      List<String> list = new ArrayList<String>();
      if (newPathList != null && newPathList.size() > 0) {
        for (String s1 : newPathList) {
          list = addToListString(s1, listString);
          if (list.size() > 1) {
            for (int i = 0; i < list.size(); i++) {
              if (list.get(i) != null) {
                List<String> list1 = addToListString(list.get(i), listString);
                for (String s : list1) {
                  oldPathList.add(s);
                }
              }
            }
          } else if (list.size() == 1) {
            oldPathList.add(list.get(0));
          }
        }
      } else {
        list = addToListString(to, listString);
        if (list.size() > 1) {
          oldPathList.clear();
          for (int i = 0; i < list.size(); i++) {
            if (list.get(i) != null) {
              List<String> list1 = addToListString(list.get(i), listString);
              for (String s : list1) {
                oldPathList.add(s);
              }
            }
          }
        } else if (list.size() == 1) {
          oldPathList.clear();
          oldPathList.add(list.get(0));
        }
      }
      j = listString.size();
    }
    return listString;
  }

  /**
   * 添加到listString中
   *
   * @param to
   * @param listString
   * @return
   */
  private List<String> addToListString(String to, List<String> listString) {
    logger.debug("<进入>:addToListString方法");
    List<String> list = new ArrayList<String>();
    for (Path p : pathList) {
      if (to.equals(p.getFrom())) {
        if (!listString.contains(p.getTo())) {
          listString.add(p.getTo());
        }
        list.add(p.getTo());
      }
    }
    return list;
  }

  private byte[] getXml() {
    logger.debug("<进入>:getXml方法");
    try {
      byte[] xml = setXmlValue(rectMap, rectList, pathList, propsList, pathMap);
      String s =
          new String(xml, "utf-8").replace("name=\"\"", "")
              .replace("assignmentHandler", "assignment-handler")
              .replace("eventListener", "event-listener");
      // .replace("<join/>", "");

      return s.getBytes("utf-8");
    } catch (Exception e) {
      logger.error("<错误>:" + e.getMessage());
    }
    return null;
  }

  private String saveData(String json, String detailId, byte[] xml) {
    logger.debug("<进入>:saveData方法");
    // 保存数据到数据库
    String flowDetail = jbpmService.save(rectList, propsList, null, null, detailId, json, xml);
    return flowDetail;
  }

  private byte[] setXmlValue(Map<String, Rect> map, List<Rect> list, List<Path> listPath,
      List<Props> listProps, Map<String, String> map1) throws JAXBException, IOException {
    logger.debug("<进入>:setXmlValue方法");
    JAXBContext context = JAXBContext.newInstance(Process.class);
    Marshaller m = context.createMarshaller();
    Label label1 = new Label();
    List<Label> listLabel = new ArrayList<Label>();
    Label label3 = new Label();
    List<Label> labelList4 = new ArrayList<Label>();
    List<Label> labelList5 = new ArrayList<Label>();
    List<Label> labelList6 = new ArrayList<Label>();
    List<Label> labelList7 = new ArrayList<Label>();

    if (list != null && list.size() > 0) {
      for (int i = 0; i < list.size(); i++) {
        if (list.get(i) != null) {
          if ("start".equals(list.get(i).getType())) {
            find(map, list, listPath, label1, i);
          }
          if ("task".equals(list.get(i).getType())) {
            Label label2 = new Label();
            // 获取该key的名称
            String keyName = "";
            for (String key : map.keySet()) {
              if (map.get(key).equals(list.get(i)))
                keyName = key;
            }
            // 先获取type为start的keyName,然后根据这个keyName寻找要加入的keyName2,如果相等说明是start下的task
            String keyName1 = "";
            String keyName2 = "";
            for (int j = 0; j < list.size(); j++) {
              if (list.get(j).getType().equals("start")) {
                for (String key : map.keySet()) {
                  if (map.get(key).equals(list.get(j)))
                    keyName1 = key;
                }
              }
            }
            if (keyName1 != null && !"".equals(keyName1)) {
              // map1.keySet():
              /**
               * from: 'rect1', to: 'rect3',
               */
              for (String key : map1.keySet()) {
                if (key.equals(keyName1))
                  keyName2 = map1.get(key);
              }
              if (keyName != null && !"".equals(keyName) && keyName2 != null
                  && !"".equals(keyName2) && keyName.equals(keyName2)) {
                label2.setAssignee("#{starter}");
              } else {
                AssignmentHandler a = new AssignmentHandler();
                a.setCLASS("com.topdt.jbpm.extension.TaskAssigner");
                label2.setAssignmentHandler(a);
              }
            }
            // add by calonlan 设置task的desc属性zhangyong201412125edit desc换位condition
            label2.setDesc(list.get(i).getProps().getCondition().getValue());
            find(map, list, listPath, label2, i);
            listLabel.add(label2);
          }
          if ("fork".equals(list.get(i).getType())) {
            Label label4 = new Label();
            find(map, list, listPath, label4, i);
            labelList4.add(label4);
          }
          if ("join".equals(list.get(i).getType())) {
            Label label5 = new Label();
            find(map, list, listPath, label5, i);
            labelList5.add(label5);
          }
          if ("end".equals(list.get(i).getType())) {
            find(map, list, listPath, label3, i);
          }
          // add by calanlan
          if ("foreach".equals(list.get(i).getType())) {
            Label label6 = new Label();
            label6.setIn("#{foreach_users}");
            label6.setVar("foreach_user");
            On on = new On();
            on.setEvent("start");
            EventListener e = new EventListener();
            e.setCLASS("com.topdt.jbpm.extension.ForeachListener");
            on.setEventListener(e);
            label6.setOn(on);
            find(map, list, listPath, label6, i);
            labelList6.add(label6);
          }
          // 类型loopjoin、join都放到labelList5中
          if ("loopjoin".equals(list.get(i).getType())) {
            Label label7 = new Label();
            label7.setMultiplicity("#{foreach_quorum}");
            find(map, list, listPath, label7, i);
            labelList5.add(label7);
          }
          // decision决定节点
          if ("decision".equals(list.get(i).getType())) {
            Label label8 = new Label();
            Handler handler = new Handler();
            handler.setCLASS("com.topdt.jbpm.extension.DecisionBranchHandler");
            label8.setHandler(handler);
            find(map, list, listPath, label8, i);
            labelList7.add(label8);
          }
        }
      }
    }
    Process process = new Process(label1, label3);
    process.setName(listProps.get(0).getName().getValue() + AppContext.getCurrentComId());
    process.setXmlns("http://jbpm.org/4.4/jpdl");
    On on = new On();
    on.setEvent("end");
    EventListener e = new EventListener();
    e.setCLASS("com.topdt.jbpm.extension.ProcessListener");
    on.setEventListener(e);
    process.setOn(on);
    process.setTask(listLabel);
    process.setFork(labelList4);
    process.setJoin(labelList5);
    process.setForeach(labelList6);
    process.setDecision(labelList7);
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    m.marshal(process, os);
    byte[] xml = os.toByteArray();
    return xml;
  }

  private static void find(Map<String, Rect> map, List<Rect> list, List<Path> listPath,
      Label label, int i) {
    // 获取对应的key值:例如rect1
    String keyName = "";
    for (String key : map.keySet()) {
      if (map.get(key).equals(list.get(i)))
        keyName = key;
    }
    if (keyName != null || !"".equals(keyName)) {
      List<Transition> listTransitions = new ArrayList<Transition>();
      for (int j = 0; j < listPath.size(); j++) {
        if (listPath.get(j).getFrom().equals(keyName)) {
          Transition transition = new Transition();
          Coord[] coord = listPath.get(j).getDots();
          StringBuilder sbu = new StringBuilder();
          for (int k = 0; k < coord.length; k++) {
            Coord c = coord[k];
            sbu.append(c.getX() + "," + c.getY());
            if (k < coord.length - 1) {
              sbu.append(";");
            } else if (k == coord.length - 1) {
              sbu.append(":");
            }
          }
          sbu.append(listPath.get(j).getTextPos().getX() + ","
              + listPath.get(j).getTextPos().getY());
          transition.setG(sbu.toString());
          transition.setName(listPath.get(j).getProps().getText().getValue());
          transition.setTo(listPath.get(j).getTo());
          listTransitions.add(transition);
        }
      }
      label.setTransition(listTransitions);
    }
    label.setG(list.get(i).getAttr().getX() + "," + list.get(i).getAttr().getY() + ","
        + list.get(i).getAttr().getWidth() + "," + list.get(i).getAttr().getHeight());
    label.setName(list.get(i).getProps().getText().getValue());
  }
}
